class FFT111 {
  constructor(bufferSize, sampleRate) {
    this.bufferSize = bufferSize;
    this.sampleRate = sampleRate;
    this.bandwidth = (2 / bufferSize) * (sampleRate / 2);
    this.real = new Float32Array(bufferSize);
    this.imag = new Float32Array(bufferSize);
    this.spectrum = new Float32Array(bufferSize / 2);

    this.reverseTable = new Uint32Array(bufferSize);
    this.sinTable = new Float32Array(bufferSize);
    this.cosTable = new Float32Array(bufferSize);

    for (let i = 0; i < bufferSize; i++) {
      let j = 0;
      for (let bit = 0; bit < Math.log2(bufferSize); bit++) {
        if (i & (1 << bit)) {
          j |= (bufferSize >> (bit + 1));
        }
      }
      this.reverseTable[i] = j;
      this.sinTable[i] = Math.sin(-Math.PI * i / bufferSize);
      this.cosTable[i] = Math.cos(-Math.PI * i / bufferSize);
    }
  }

  forward(buffer) {
    const real = this.real;
    const imag = this.imag;
    const reverseTable = this.reverseTable;
    const sinTable = this.sinTable;
    const cosTable = this.cosTable;
    const bufferSize = this.bufferSize;

    for (let i = 0; i < bufferSize; i++) {
      real[i] = buffer[reverseTable[i]];
      imag[i] = 0;
    }

    for (let halfSize = 1; halfSize < bufferSize; halfSize *= 2) {
      const phaseShiftStepReal = cosTable[halfSize];
      const phaseShiftStepImag = sinTable[halfSize];

      for (let fftStep = 0; fftStep < halfSize; fftStep++) {
        const currentPhaseShiftReal = cosTable[fftStep];
        const currentPhaseShiftImag = sinTable[fftStep];

        for (let i = fftStep; i < bufferSize; i += halfSize * 2) {
          const off = i + halfSize;
          const tr = currentPhaseShiftReal * real[off] - currentPhaseShiftImag * imag[off];
          const ti = currentPhaseShiftReal * imag[off] + currentPhaseShiftImag * real[off];

          real[off] = real[i] - tr;
          imag[off] = imag[i] - ti;
          real[i] += tr;
          imag[i] += ti;
        }
      }
    }

    const spectrum = this.spectrum;
    for (let i = 0, N = bufferSize / 2; i < N; i++) {
      spectrum[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) / bufferSize;
      if (isNaN(spectrum[i])) {
        console.error(`Spectrum NaN at index ${i}`, real[i], imag[i]);
      }
    }

    return spectrum;
  }
}

module.exports = FFT111;

